package com.miyake.dsp.analyzer;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;

import com.miyake.dsp.common.CommonTool;
import com.miyake.dsp.common.EdgeDetector;
import com.miyake.dsp.common.EdgeDetector.Edge;
import com.miyake.dsp.osa.lab.EdgeNotFoundException;
import com.miyake.dsp.osa.lab.NoiseDetector;
import com.miyake.dsp.osa.lab.NoiseDetector2;
import com.miyake.dsp.osa.lab.Sort;
import com.miyake.dsp.ui.LineChartPanel;

public class PeakSearchCommon {
	enum Status {
		OutOfSignal,
		StartSignal,
		InSignal1,
		InSignal2,
		AfterSignal
	}
	private List<Integer> peakIndexes = new LinkedList<Integer>();
	private double noiseLevel;
	private int minSpanThreshold;
	
	private void initialize(double[] data, Double noiseLevel) {
		double[] smo = CommonTool.getSmoothed(data, data.length/100);
		
		
//		LineChartPanel.showChart(diff, "diff");
//		double[] lap = CommonTool.getDiff(diff/*CommonTool.getSmoothed(diff, diff.length/50)*/);
//		int peakIndex = 0;
		double max = Double.MIN_VALUE;
		Status status = Status.OutOfSignal;
		
		double min = 1e-6;//Math.pow(10.0, -minimum/10.0);

		if (noiseLevel == null) {
			noiseLevel = 1e-6;
/*			try {
				NoiseDetector noiseDetector = new NoiseDetector();
				noiseDetector.setData(data);
				noiseLevel = noiseDetector.getNoiseFloor();

			} catch (EdgeNotFoundException e) {
				e.printStackTrace();
			}	*/
			NoiseDetector2 noiseDetector = new NoiseDetector2(data);
			noiseLevel = noiseDetector.getNoiseLevel();
			this.noiseLevel = noiseLevel.doubleValue() + (CommonTool.getMaxValue(data) - noiseLevel.doubleValue()) / 2000.0;
		}
		else {
			this.noiseLevel = noiseLevel.doubleValue();
		}

		
		
/*		try {
			diff = CommonTool.getAbsolute(diff);
			NoiseDetector noiseDetector = new NoiseDetector();
			noiseDetector.setData(diff);
			min = noiseDetector.getNoiseFloor() + CommonTool.getMaxValue(diff) / 5;
		} catch (EdgeNotFoundException e) {
			e.printStackTrace();
		}*/
		
		//double[] diff = CommonTool.getDiff(CommonTool.getFloor(smo, noiseLevel));
		double[] diff = CommonTool.getDiff(smo);
		NoiseDetector2 noiseDetector = new NoiseDetector2(diff);
		min = noiseDetector.getNoiseLevel() + CommonTool.getMaxValue(diff)/ 5;
		List<Integer> widths = new ArrayList<Integer>();
		double incrementStep = (CommonTool.getMaxValue(data) - noiseLevel.doubleValue()) / 2000.0;
		
		for (int loop = 0; loop < 1000; loop++) {
			int iStart = 0;
			widths.clear();
			peakIndexes.clear();
			
			for (int i = 0; i < diff.length; i++) {
				if (isFlag()) {
					if (status.equals(Status.OutOfSignal)) {
						if (data[i] > this.noiseLevel) {
							status = Status.StartSignal;
							iStart = i;
						}
					}
					else if (status.equals(Status.StartSignal)) {
						if (data[i] < this.noiseLevel && ((i-iStart) > minSpanThreshold)) {
							status = Status.OutOfSignal;
							peakIndexes.add(CommonTool.getPeakIndex(data, iStart, i));
							widths.add(i-iStart);
						}
						else if (data[i] > this.noiseLevel) {
							status = Status.StartSignal;
						}
						else {
							status = Status.OutOfSignal;
						}
					}
				}
				else {
					if (status.equals(Status.OutOfSignal)) {
						if (diff[i] > min && data[i] > noiseLevel) {
							status = Status.StartSignal;
						}
					}
					else if (status.equals(Status.StartSignal)) {
						if (diff[i] < min) {
							status = Status.InSignal1;
							iStart = i;
							max = Double.MIN_VALUE;
						}
		
					}
					else if (status.equals(Status.InSignal1)) {
						if (diff[i] > min) {
							status = Status.AfterSignal;
						}
						if (data[i] > max) {
							max = data[i];
						}
					}
					else if (status.equals(Status.AfterSignal)) {
						if (diff[i] < min) {
							status = Status.OutOfSignal;
							peakIndexes.add(CommonTool.getPeakIndex(data, iStart, i));
						}
					}
				}
			}
	
			if (containsNoise(peakIndexes, widths)) {
				this.noiseLevel += incrementStep;
				System.out.println("AGAIN " + loop);
			}
			else {
				break;
			}
		}
	}
	
	private boolean isFlag() {
		return true;
	}

	private boolean containsNoise(List<Integer> peakIndexes2, List<Integer> widths) {
		if (peakIndexes2.size() == 1) {
			return false;
		}
		int[] indexArray = ArrayUtils.toPrimitive(peakIndexes.toArray(new Integer[0]));
		int[] widthArray = ArrayUtils.toPrimitive(widths.toArray(new Integer[0]));
		int maxWidth = CommonTool.getMaxValue(widthArray);
		int minWidth = CommonTool.getMinValue(widthArray);
		
		if (maxWidth / minWidth > 5) {
			int inc = Math.max(minWidth, maxWidth/5);
			minSpanThreshold  += inc;
		}
		
		for (int i = 1; i < indexArray.length; i++) {
			if (indexArray[i] - indexArray[i-1] < maxWidth) {
				return true;
			}
		}
		return false;
	}

	public PeakSearchCommon(double[] data) {
		initialize(data, null);
	}
	
	public PeakSearchCommon(double[] data, Double noiseLevel) {
		initialize(data, noiseLevel);
	}
	
	public List<Integer> getPeakInexes() {
		return peakIndexes;
	}

	public double getNoiseLevel() {
		return this.noiseLevel;
	}
}
